{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Аггрегация разметки датасета ruDetox"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Аггрегация строится по следующей системе:\n",
    "\n",
    "1. Сбор размеченных пулов с Толоки. Возможны варианты:\n",
    "    - только общий пул нужно аггрегировать, тогда забирается только он\n",
    "    - часть данных находится в контрольных заданиях и экзамене, тогда к основному пулу добавляются данные задания\n",
    "2. Фильтрация разметчиков:\n",
    "    - в общем пуле есть некоторое количество заранее размеченных заданий - контрольных\n",
    "    - хорошим считается разметчик, который показывает `accuracy >= 0.5` на данных заданиях\n",
    "    - формируется список \"плохих\" разметчиков\n",
    "3. Аггрегация ответов разметчиков по заданиям:\n",
    "    - форматирование в заданиях может отличаться от изначального из-за выгрузки с Толоки\n",
    "    - учитываются только ответы \"хороших\" разметчиков\n",
    "    - аггрегация по подготовленным пулам - создается массив карточек вида {key: value}, где key - кортеж из всех значимых элементов задания, value - список из кортежей вида (user_id, answer)\n",
    "4. Голосование большинством по каждому заданию:\n",
    "    - минимально необходимое большинство составляет 3 голоса, так как такое большинство валидно для перекрытия 5\n",
    "    - по результату формируется датафрейм с заданиями и ответами\n",
    "5. Подгрузка оригинальных данных с разметкой в виде таблицы с заданиями и ответами\n",
    "6. Соединение таблиц:\n",
    "    - очистка форматирования в таблице с ответами разметчиков и в таблице с правильными ответами\n",
    "    - создание единых столбцов с полным заданием\n",
    "    - соединение таблиц по данному столбцу\n",
    "    - валидация размеров\n",
    "7. Подсчет метрик"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Данные для разметки были взяты из публичного теста датасета Russe Detox. Всего в выборке было 800 примеров. \n",
    "\n",
    "Датасет представляет из себя пары текстов:\n",
    "- токсичный текст\n",
    "- аналогичный текст без признаков токсичности"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Датасет фильтровался в трех независимых проектах на платформе Яндекс.Толока. Каждый проект проверял одно свойство ответов публичного теста датасета:\n",
    "\n",
    "(1) оскорбительность “детоксифицированного” текста\n",
    "\n",
    "(2) связность “детоксифицированного” текста\n",
    "\n",
    "(3) совпадение смысла “детоксифицированного” текста со своей токсичной версией."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "C:\\Users\\artem\\AppData\\Roaming\\Python\\Python311\\site-packages\\sklearn\\base.py:347: InconsistentVersionWarning: Trying to unpickle estimator IsotonicRegression from version 1.0 when using version 1.3.0. This might lead to breaking code or invalid results. Use at your own risk. For more info please refer to:\n",
      "https://scikit-learn.org/stable/model_persistence.html#security-maintainability-limitations\n",
      "  warnings.warn(\n"
     ]
    }
   ],
   "source": [
    "from eval import load_model, evaluate_style_transfer, load_csv\n",
    "import pandas as pd\n",
    "import pickle\n",
    "import numpy as np\n",
    "import json\n",
    "from collections import Counter"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Проект №1. Проверка связности и грамотности текстов"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>INPUT:idx</th>\n",
       "      <th>INPUT:neutral_comment</th>\n",
       "      <th>INPUT:training_counter</th>\n",
       "      <th>OUTPUT:fluent</th>\n",
       "      <th>GOLDEN:fluent</th>\n",
       "      <th>HINT:text</th>\n",
       "      <th>HINT:default_language</th>\n",
       "      <th>ASSIGNMENT:link</th>\n",
       "      <th>ASSIGNMENT:task_id</th>\n",
       "      <th>ASSIGNMENT:assignment_id</th>\n",
       "      <th>ASSIGNMENT:task_suite_id</th>\n",
       "      <th>ASSIGNMENT:worker_id</th>\n",
       "      <th>ASSIGNMENT:status</th>\n",
       "      <th>ASSIGNMENT:started</th>\n",
       "      <th>ASSIGNMENT:submitted</th>\n",
       "      <th>ASSIGNMENT:accepted</th>\n",
       "      <th>ASSIGNMENT:reward</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>249</td>\n",
       "      <td>Ну и зачем вы сейчас выкинули это видео2015год...</td>\n",
       "      <td>1343</td>\n",
       "      <td>partly</td>\n",
       "      <td>NaN</td>\n",
       "      <td>NaN</td>\n",
       "      <td>NaN</td>\n",
       "      <td>https://platform.toloka.ai/task/41793535/00027...</td>\n",
       "      <td>00027db7ff--652c79695dae053080c93ed9</td>\n",
       "      <td>00027db7ff--652c80ffe013ff63cff0532b</td>\n",
       "      <td>00027db7ff--652c80fee013ff63cff05325</td>\n",
       "      <td>78c9275a4ea4f3a6421a3e5be36dc6a2</td>\n",
       "      <td>APPROVED</td>\n",
       "      <td>2023-10-16T00:17:03.055</td>\n",
       "      <td>2023-10-16T00:18:08.230</td>\n",
       "      <td>2023-10-16T00:18:08.230</td>\n",
       "      <td>0.03</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "   INPUT:idx                              INPUT:neutral_comment  \\\n",
       "0        249  Ну и зачем вы сейчас выкинули это видео2015год...   \n",
       "\n",
       "   INPUT:training_counter OUTPUT:fluent GOLDEN:fluent  HINT:text  \\\n",
       "0                    1343        partly           NaN        NaN   \n",
       "\n",
       "   HINT:default_language                                    ASSIGNMENT:link  \\\n",
       "0                    NaN  https://platform.toloka.ai/task/41793535/00027...   \n",
       "\n",
       "                     ASSIGNMENT:task_id              ASSIGNMENT:assignment_id  \\\n",
       "0  00027db7ff--652c79695dae053080c93ed9  00027db7ff--652c80ffe013ff63cff0532b   \n",
       "\n",
       "               ASSIGNMENT:task_suite_id              ASSIGNMENT:worker_id  \\\n",
       "0  00027db7ff--652c80fee013ff63cff05325  78c9275a4ea4f3a6421a3e5be36dc6a2   \n",
       "\n",
       "  ASSIGNMENT:status       ASSIGNMENT:started     ASSIGNMENT:submitted  \\\n",
       "0          APPROVED  2023-10-16T00:17:03.055  2023-10-16T00:18:08.230   \n",
       "\n",
       "       ASSIGNMENT:accepted  ASSIGNMENT:reward  \n",
       "0  2023-10-16T00:18:08.230               0.03  "
      ]
     },
     "execution_count": 2,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "assignments = pd.read_csv('assignments_from_pool_41793535__21-10-2023.tsv', sep='\\t')\n",
    "assignments.head(1)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Фильтруем толокеров, которые дали меньше половины корректных ответов на контрольных заданиях."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Users total:  372\n",
      "Bad users: 201\n"
     ]
    }
   ],
   "source": [
    "from collections import defaultdict\n",
    "\n",
    "users_dict = defaultdict(lambda: defaultdict(int))\n",
    "\n",
    "for idx, row in assignments.iterrows():\n",
    "    text = row[1]\n",
    "\n",
    "    out = row[3]\n",
    "    \n",
    "    gold = row[4]\n",
    "\n",
    "    user = row[11]\n",
    "\n",
    "    if str(user) != \"nan\" and str(gold) != \"nan\":\n",
    "        if out == gold:\n",
    "            users_dict[user][\"good\"] += 1\n",
    "        else:\n",
    "            users_dict[user][\"bad\"] += 1\n",
    "\n",
    "print(\"Users total: \", len(users_dict))\n",
    "bad_users = []\n",
    "for key, value in users_dict.items():\n",
    "    percentage_good = value[\"good\"]/(value[\"good\"] + value[\"bad\"])\n",
    "    if percentage_good < 0.5:\n",
    "        bad_users.append(key)\n",
    "\n",
    "print(\"Bad users:\", len(bad_users))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "201 из 372 разметчиков на контрольных заданиях показали слишком плохое качество, чтобы учитывать их ответы для расчета метрики."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Теперь нужно оставить только основной пул. Контрольные задания создавались вручную из отбракованных ранее примеров, чтобы не было пересечений с тестсетом. На контрольных заданиях есть `GOLDEN:fluent`. Также отсеиваем возможные баги Толоки, когда в строке может не быть задания - `INPUT:neutral_comment` содержит NaN."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "assignments_no_control = assignments[assignments['GOLDEN:fluent'].isnull()]\n",
    "assignments_no_control_no_null = assignments_no_control[assignments_no_control['INPUT:neutral_comment'].notnull()]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Собираем ответы голосования большинством для каждого задания."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "797\n"
     ]
    }
   ],
   "source": [
    "from collections import defaultdict\n",
    "\n",
    "text_dict = defaultdict(list)\n",
    "\n",
    "for text, user, out in zip(\n",
    "    assignments_no_control_no_null[\"INPUT:neutral_comment\"], assignments_no_control_no_null[\"ASSIGNMENT:worker_id\"], \n",
    "    assignments_no_control_no_null[\"OUTPUT:fluent\"]\n",
    "    ):\n",
    "    if user not in bad_users:\n",
    "        text_dict[text].append([\n",
    "                user,\n",
    "                {\"out\": out}\n",
    "        ])\n",
    "\n",
    "print(len(text_dict))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Counter({4: 293, 3: 292, 2: 108, 5: 83, 1: 21})"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "keys = list(text_dict.keys())\n",
    "Counter([len(text_dict[keys[i]]) for i in range(len(keys))])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Только в 83 заданиях перекрытие составило 5 человек. Ко всем заданиям будем применять правило, что большинство должно составить минимум 3 человека для формирования метки по результатам голосования большинством. В заданиях с перекрытием меньше 3 такое правило автоматически невыполнимо."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [],
   "source": [
    "preds_full = {}\n",
    "for i in range(len(keys)):\n",
    "    ans = text_dict[keys[i]]\n",
    "    lst = [ans[j][1]['out'] for j in range(len(ans))]\n",
    "    cnt = Counter(lst)\n",
    "    most = Counter([ans[j][1]['out'] for j in range(len(ans))]).most_common(1)[0][1]\n",
    "    if most >= 3:\n",
    "        res = Counter([ans[j][1]['out'] for j in range(len(ans))]).most_common(1)[0][0]\n",
    "        preds_full[keys[i]] = res"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "561"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "len(preds_full)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Отфильтровались 239 заданий."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [],
   "source": [
    "preds_full_df = pd.concat([pd.DataFrame(preds_full.keys(), columns=['text',]), pd.DataFrame(preds_full.values(), columns=['lb'])], axis=1)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Для упрощения последующей аггрегации соединим полученную разметку сс оригинальным датасетом."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [],
   "source": [
    "res_df = pd.read_csv('dataset.csv')\n",
    "res_df = res_df.rename({'outputs': 'text'}, axis=1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [],
   "source": [
    "def format_text(text):\n",
    "    text = (text.strip().replace('\\n', ' ').replace('\\t', ' ')\n",
    "            .replace('\\r', ' ').replace('  ', ' ').replace('  ', ' ')\n",
    "            .replace('  ', ' '))\n",
    "    return text\n",
    "\n",
    "res_df['text'] = res_df['text'].apply(format_text)\n",
    "preds_full_df['text'] = preds_full_df['text'].apply(format_text)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Делаем left join, чтобы соединить голосование и поригинальные тексты."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [],
   "source": [
    "new = res_df.merge(preds_full_df, on='text', how='left')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "239"
      ]
     },
     "execution_count": 13,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "new['lb'].isna().sum()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "NaN'ы в отфильтрованных 239 заданиях. Сохраняем в отдельную переменную результаты разметки."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [],
   "source": [
    "lit = new.iloc[:, 1:].reset_index(drop=True).copy()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Проект №2. Проверка токсичности текстов"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Схема работы с данным и третьим проектом аналогична первому."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>INPUT:idx</th>\n",
       "      <th>INPUT:task1_suite_id</th>\n",
       "      <th>INPUT:neutral_comment</th>\n",
       "      <th>INPUT:training_counter</th>\n",
       "      <th>OUTPUT:toxic</th>\n",
       "      <th>GOLDEN:toxic</th>\n",
       "      <th>HINT:text</th>\n",
       "      <th>HINT:default_language</th>\n",
       "      <th>ASSIGNMENT:link</th>\n",
       "      <th>ASSIGNMENT:task_id</th>\n",
       "      <th>ASSIGNMENT:assignment_id</th>\n",
       "      <th>ASSIGNMENT:task_suite_id</th>\n",
       "      <th>ASSIGNMENT:worker_id</th>\n",
       "      <th>ASSIGNMENT:status</th>\n",
       "      <th>ASSIGNMENT:started</th>\n",
       "      <th>ASSIGNMENT:submitted</th>\n",
       "      <th>ASSIGNMENT:accepted</th>\n",
       "      <th>ASSIGNMENT:reward</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>518</td>\n",
       "      <td>1</td>\n",
       "      <td>Зачем вообще эта дума нужна</td>\n",
       "      <td>1</td>\n",
       "      <td>False</td>\n",
       "      <td>NaN</td>\n",
       "      <td>NaN</td>\n",
       "      <td>NaN</td>\n",
       "      <td>https://platform.toloka.ai/task/41793133/00027...</td>\n",
       "      <td>00027db66d--652c6aa0ac0d3c5e4b2bdcde</td>\n",
       "      <td>00027db66d--652c6d259404dc38e65a3f7a</td>\n",
       "      <td>00027db66d--652c6d259404dc38e65a3f78</td>\n",
       "      <td>6172cce8a382906f77a3d64bafdd3a27</td>\n",
       "      <td>APPROVED</td>\n",
       "      <td>2023-10-15T22:52:21.837</td>\n",
       "      <td>2023-10-15T22:52:43.876</td>\n",
       "      <td>2023-10-15T22:52:43.876</td>\n",
       "      <td>0.03</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "   INPUT:idx INPUT:task1_suite_id        INPUT:neutral_comment  \\\n",
       "0        518                    1  Зачем вообще эта дума нужна   \n",
       "\n",
       "   INPUT:training_counter  OUTPUT:toxic GOLDEN:toxic  HINT:text  \\\n",
       "0                       1         False          NaN        NaN   \n",
       "\n",
       "   HINT:default_language                                    ASSIGNMENT:link  \\\n",
       "0                    NaN  https://platform.toloka.ai/task/41793133/00027...   \n",
       "\n",
       "                     ASSIGNMENT:task_id              ASSIGNMENT:assignment_id  \\\n",
       "0  00027db66d--652c6aa0ac0d3c5e4b2bdcde  00027db66d--652c6d259404dc38e65a3f7a   \n",
       "\n",
       "               ASSIGNMENT:task_suite_id              ASSIGNMENT:worker_id  \\\n",
       "0  00027db66d--652c6d259404dc38e65a3f78  6172cce8a382906f77a3d64bafdd3a27   \n",
       "\n",
       "  ASSIGNMENT:status       ASSIGNMENT:started     ASSIGNMENT:submitted  \\\n",
       "0          APPROVED  2023-10-15T22:52:21.837  2023-10-15T22:52:43.876   \n",
       "\n",
       "       ASSIGNMENT:accepted  ASSIGNMENT:reward  \n",
       "0  2023-10-15T22:52:43.876               0.03  "
      ]
     },
     "execution_count": 15,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "assignments = pd.read_csv('assignments_from_pool_41793133__17-10-2023.tsv', sep='\\t')\n",
    "assignments.head(1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Users total:  341\n",
      "Bad users: 158\n"
     ]
    }
   ],
   "source": [
    "from collections import defaultdict\n",
    "\n",
    "users_dict = defaultdict(lambda: defaultdict(int))\n",
    "\n",
    "for idx, row in assignments.iterrows():\n",
    "    text = row[2]\n",
    "\n",
    "    out = row[4]\n",
    "    \n",
    "    gold = row[5]\n",
    "\n",
    "    user = row[12]\n",
    "\n",
    "    if str(user) != \"nan\" and str(gold) != \"nan\":\n",
    "        if out == int(gold):\n",
    "            users_dict[user][\"good\"] += 1\n",
    "        else:\n",
    "            users_dict[user][\"bad\"] += 1\n",
    "\n",
    "print(\"Users total: \", len(users_dict))\n",
    "bad_users = []\n",
    "for key, value in users_dict.items():\n",
    "    percentage_good = value[\"good\"]/(value[\"good\"] + value[\"bad\"])\n",
    "    if percentage_good < 0.5:\n",
    "        bad_users.append(key)\n",
    "\n",
    "print(\"Bad users:\", len(bad_users))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "158 из 341 разметчиков на контрольных заданиях показали слишком плохое качество, чтобы учитывать их ответы для расчета метрики."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [],
   "source": [
    "assignments_no_control = assignments[assignments['GOLDEN:toxic'].isnull()]\n",
    "assignments_no_control_no_null = assignments_no_control[assignments_no_control['INPUT:neutral_comment'].notnull()]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "800\n"
     ]
    }
   ],
   "source": [
    "from collections import defaultdict\n",
    "\n",
    "text_dict = defaultdict(list)\n",
    "\n",
    "for text, user, out in zip(\n",
    "    assignments_no_control_no_null[\"INPUT:neutral_comment\"], assignments_no_control_no_null[\"ASSIGNMENT:worker_id\"], \n",
    "    assignments_no_control_no_null[\"OUTPUT:toxic\"]\n",
    "    ):\n",
    "    if user not in bad_users:\n",
    "        text_dict[text].append([\n",
    "                user,\n",
    "                {\"out\": out}\n",
    "        ])\n",
    "\n",
    "print(len(text_dict))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Counter({4: 355, 5: 235, 3: 183, 2: 25, 1: 2})"
      ]
     },
     "execution_count": 19,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "keys = list(text_dict.keys())\n",
    "Counter([len(text_dict[keys[i]]) for i in range(len(keys))])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "В 235 заданиях перекрытие 5. Голосование большинством проводится при наличии минимум 3 человек в таком большинстве."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [],
   "source": [
    "preds_full = {}\n",
    "for i in range(len(keys)):\n",
    "    ans = text_dict[keys[i]]\n",
    "    lst = [ans[j][1]['out'] for j in range(len(ans))]\n",
    "    cnt = Counter(lst)\n",
    "    most = Counter([ans[j][1]['out'] for j in range(len(ans))]).most_common(1)[0][1]\n",
    "    if most >= 3:\n",
    "        res = Counter([ans[j][1]['out'] for j in range(len(ans))]).most_common(1)[0][0]\n",
    "        preds_full[keys[i]] = res"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "698"
      ]
     },
     "execution_count": 21,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "len(preds_full)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Всего в 102 текстах согласованность не была достигнута."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [],
   "source": [
    "preds_full_df = pd.concat([pd.DataFrame(preds_full.keys(), columns=['text',]), pd.DataFrame(preds_full.values(), columns=['lb'])], axis=1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {},
   "outputs": [],
   "source": [
    "res_df = pd.read_csv('dataset.csv')\n",
    "res_df = res_df.rename({'outputs': 'text'}, axis=1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {},
   "outputs": [],
   "source": [
    "res_df['text'] = res_df['text'].apply(format_text)\n",
    "preds_full_df['text'] = preds_full_df['text'].apply(format_text)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {},
   "outputs": [],
   "source": [
    "new = res_df.merge(preds_full_df, on='text', how='left')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "102"
      ]
     },
     "execution_count": 26,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "new['lb'].isna().sum()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "NaN'ы в 102 текстах. Значит, аггрегация проведена корректно и не было потеряно меток."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "metadata": {},
   "outputs": [],
   "source": [
    "ins = new.iloc[:, 1:].reset_index(drop=True).copy()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Проект №3. Проверка смысловой идентичности текстов"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>INPUT:idx</th>\n",
       "      <th>INPUT:toxic_comment</th>\n",
       "      <th>INPUT:task1_suite_id</th>\n",
       "      <th>INPUT:neutral_comment</th>\n",
       "      <th>INPUT:training_counter</th>\n",
       "      <th>OUTPUT:is_match</th>\n",
       "      <th>GOLDEN:is_match</th>\n",
       "      <th>HINT:text</th>\n",
       "      <th>HINT:default_language</th>\n",
       "      <th>ASSIGNMENT:link</th>\n",
       "      <th>ASSIGNMENT:task_id</th>\n",
       "      <th>ASSIGNMENT:assignment_id</th>\n",
       "      <th>ASSIGNMENT:task_suite_id</th>\n",
       "      <th>ASSIGNMENT:worker_id</th>\n",
       "      <th>ASSIGNMENT:status</th>\n",
       "      <th>ASSIGNMENT:started</th>\n",
       "      <th>ASSIGNMENT:submitted</th>\n",
       "      <th>ASSIGNMENT:accepted</th>\n",
       "      <th>ASSIGNMENT:reward</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>160</td>\n",
       "      <td>такого долбоебизма как в киеве не будет</td>\n",
       "      <td>1</td>\n",
       "      <td>такого, как в киеве, не будет.</td>\n",
       "      <td>198</td>\n",
       "      <td>False</td>\n",
       "      <td>NaN</td>\n",
       "      <td>NaN</td>\n",
       "      <td>NaN</td>\n",
       "      <td>https://platform.toloka.ai/task/41793355/00027...</td>\n",
       "      <td>00027db74b--652c72bba8ebdc7126d23fa7</td>\n",
       "      <td>00027db74b--652c758e9404dc38e65bc5bf</td>\n",
       "      <td>00027db74b--652c758e9404dc38e65bc5bc</td>\n",
       "      <td>d171b547bedef18946541e2a2a6ff829</td>\n",
       "      <td>APPROVED</td>\n",
       "      <td>2023-10-15T23:28:14.348</td>\n",
       "      <td>2023-10-15T23:28:55.483</td>\n",
       "      <td>2023-10-15T23:28:55.483</td>\n",
       "      <td>0.03</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "   INPUT:idx                      INPUT:toxic_comment INPUT:task1_suite_id  \\\n",
       "0        160  такого долбоебизма как в киеве не будет                    1   \n",
       "\n",
       "            INPUT:neutral_comment  INPUT:training_counter  OUTPUT:is_match  \\\n",
       "0  такого, как в киеве, не будет.                     198            False   \n",
       "\n",
       "  GOLDEN:is_match  HINT:text  HINT:default_language  \\\n",
       "0             NaN        NaN                    NaN   \n",
       "\n",
       "                                     ASSIGNMENT:link  \\\n",
       "0  https://platform.toloka.ai/task/41793355/00027...   \n",
       "\n",
       "                     ASSIGNMENT:task_id              ASSIGNMENT:assignment_id  \\\n",
       "0  00027db74b--652c72bba8ebdc7126d23fa7  00027db74b--652c758e9404dc38e65bc5bf   \n",
       "\n",
       "               ASSIGNMENT:task_suite_id              ASSIGNMENT:worker_id  \\\n",
       "0  00027db74b--652c758e9404dc38e65bc5bc  d171b547bedef18946541e2a2a6ff829   \n",
       "\n",
       "  ASSIGNMENT:status       ASSIGNMENT:started     ASSIGNMENT:submitted  \\\n",
       "0          APPROVED  2023-10-15T23:28:14.348  2023-10-15T23:28:55.483   \n",
       "\n",
       "       ASSIGNMENT:accepted  ASSIGNMENT:reward  \n",
       "0  2023-10-15T23:28:55.483               0.03  "
      ]
     },
     "execution_count": 28,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "assignments = pd.read_csv('assignments_from_pool_41793355__21-10-2023.tsv', sep='\\t')\n",
    "assignments.head(1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Users total:  172\n",
      "Bad users: 37\n"
     ]
    }
   ],
   "source": [
    "from collections import defaultdict\n",
    "\n",
    "users_dict = defaultdict(lambda: defaultdict(int))\n",
    "\n",
    "for idx, row in assignments.iterrows():\n",
    "    text = row[1]\n",
    "\n",
    "    out = row[5]\n",
    "    \n",
    "    gold = row[6]\n",
    "\n",
    "    user = row[13]\n",
    "\n",
    "    if str(user) != \"nan\" and str(gold) != \"nan\":\n",
    "        if out == int(gold):\n",
    "            users_dict[user][\"good\"] += 1\n",
    "        else:\n",
    "            users_dict[user][\"bad\"] += 1\n",
    "\n",
    "print(\"Users total: \", len(users_dict))\n",
    "bad_users = []\n",
    "for key, value in users_dict.items():\n",
    "    percentage_good = value[\"good\"]/(value[\"good\"] + value[\"bad\"])\n",
    "    if percentage_good < 0.5:\n",
    "        bad_users.append(key)\n",
    "\n",
    "print(\"Bad users:\", len(bad_users))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "37 из 172 разметчиков на контрольных заданиях показали слишком плохое качество, чтобы учитывать их ответы для расчета метрики."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "metadata": {},
   "outputs": [],
   "source": [
    "assignments_no_control = assignments[assignments['GOLDEN:is_match'].isnull()]\n",
    "assignments_no_control_no_null = assignments_no_control[assignments_no_control['INPUT:toxic_comment'].notnull()]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "800\n"
     ]
    }
   ],
   "source": [
    "from collections import defaultdict\n",
    "\n",
    "text_dict = defaultdict(list)\n",
    "\n",
    "for neut, tox, user, out in zip(\n",
    "    assignments_no_control_no_null[\"INPUT:neutral_comment\"], assignments_no_control_no_null[\"INPUT:toxic_comment\"], \n",
    "    assignments_no_control_no_null[\"ASSIGNMENT:worker_id\"], assignments_no_control_no_null[\"OUTPUT:is_match\"]\n",
    "    ):\n",
    "    if user not in bad_users:\n",
    "        text_dict[(neut, tox)].append([\n",
    "                user,\n",
    "                {\"out\": out}\n",
    "        ])\n",
    "\n",
    "print(len(text_dict))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 32,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Counter({5: 683, 4: 112, 3: 5})"
      ]
     },
     "execution_count": 32,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "keys = list(text_dict.keys())\n",
    "Counter([len(text_dict[keys[i]]) for i in range(len(keys))])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Только в 117 текстах перекрытие меньше 5. Правило остается прежним: большинство из 3 человек формирует оценку."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 33,
   "metadata": {},
   "outputs": [],
   "source": [
    "preds_full = {}\n",
    "for i in range(len(keys)):\n",
    "    ans = text_dict[keys[i]]\n",
    "    lst = [ans[j][1]['out'] for j in range(len(ans))]\n",
    "    cnt = Counter(lst)\n",
    "    most = Counter([ans[j][1]['out'] for j in range(len(ans))]).most_common(1)[0][1]\n",
    "    if most >= 3:\n",
    "        res = Counter([ans[j][1]['out'] for j in range(len(ans))]).most_common(1)[0][0]\n",
    "        preds_full[keys[i]] = res"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 34,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "789"
      ]
     },
     "execution_count": 34,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "len(preds_full)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Отфильтровано всего 11 текстов."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 35,
   "metadata": {},
   "outputs": [],
   "source": [
    "preds_full_df = pd.concat([pd.DataFrame(preds_full.keys(), columns=['text', 'tox']), pd.DataFrame(preds_full.values(), columns=['lb'])], axis=1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 36,
   "metadata": {},
   "outputs": [],
   "source": [
    "res_df = pd.read_csv('dataset.csv')\n",
    "res_df = res_df.rename({'outputs': 'text'}, axis=1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 37,
   "metadata": {},
   "outputs": [],
   "source": [
    "res_df['text'] = res_df['text'].apply(format_text)\n",
    "preds_full_df['text'] = preds_full_df['text'].apply(format_text)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 38,
   "metadata": {},
   "outputs": [],
   "source": [
    "new = res_df.merge(preds_full_df, on='text', how='left')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 39,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "11"
      ]
     },
     "execution_count": 39,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "new['lb'].isna().sum()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Те же 11 текстов имеют NaN'ы теперь."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 40,
   "metadata": {},
   "outputs": [],
   "source": [
    "sen = new.iloc[:, 1:].reset_index(drop=True).copy()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Аггрегация результатов разметки"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 41,
   "metadata": {},
   "outputs": [],
   "source": [
    "ins = ins.rename({'lb': 'insult'}, axis=1)\n",
    "sen = sen.rename({'lb': 'sense'}, axis=1)\n",
    "lit = lit.rename({'lb': 'literacy'}, axis=1)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Объединяем всю разметку по сохраненной колонке оригинальных текстов."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 42,
   "metadata": {},
   "outputs": [],
   "source": [
    "df = ins.merge(sen, on='inputs').merge(lit, on='inputs')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 43,
   "metadata": {},
   "outputs": [],
   "source": [
    "df = df.drop(['text_y', 'tox', 'text'], axis=1).rename({'inputs': 'init', 'text_x': 'rewrite'}, axis=1)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Оставляем только пары текстов с непустой разметкой по всем трем проектам."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 44,
   "metadata": {},
   "outputs": [],
   "source": [
    "sub = df[(df['insult'].notna()) & (df['sense'].notna()) & (df['literacy'].notna())].copy()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 45,
   "metadata": {},
   "outputs": [],
   "source": [
    "df = sub[(sub['insult'] == False) & (sub['sense'] == True) & (sub['literacy'] == 'fluent')].copy()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Оставляем только неоскорбительные, связные и грамотные тексты, которые связаны по смыслу со своими токсичными парами."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 46,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>init</th>\n",
       "      <th>rewrite</th>\n",
       "      <th>insult</th>\n",
       "      <th>sense</th>\n",
       "      <th>literacy</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>пиздеж! температуры горения хватит чтобы её ра...</td>\n",
       "      <td>Враньё! Температуры горения хватит чтобы ее ра...</td>\n",
       "      <td>False</td>\n",
       "      <td>True</td>\n",
       "      <td>fluent</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>а ты чмо там был.ты вообще служил.гандон</td>\n",
       "      <td>А ты там был? Ты вообще служил?</td>\n",
       "      <td>False</td>\n",
       "      <td>True</td>\n",
       "      <td>fluent</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>пиздабол ---- а сам где кормишься ?</td>\n",
       "      <td>а сам где кормишься ?</td>\n",
       "      <td>False</td>\n",
       "      <td>True</td>\n",
       "      <td>fluent</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>этому сайту я давно не доверяю, пишут разную х...</td>\n",
       "      <td>Этому сайту давно не доверяю, пишут всякую ерунду</td>\n",
       "      <td>False</td>\n",
       "      <td>True</td>\n",
       "      <td>fluent</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>у меня сегодня подобный звонок был. достали го...</td>\n",
       "      <td>у меня сегодня подобный звонок был. Достали.</td>\n",
       "      <td>False</td>\n",
       "      <td>True</td>\n",
       "      <td>fluent</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "                                                init  \\\n",
       "0  пиздеж! температуры горения хватит чтобы её ра...   \n",
       "1           а ты чмо там был.ты вообще служил.гандон   \n",
       "2                пиздабол ---- а сам где кормишься ?   \n",
       "3  этому сайту я давно не доверяю, пишут разную х...   \n",
       "4  у меня сегодня подобный звонок был. достали го...   \n",
       "\n",
       "                                             rewrite insult sense literacy  \n",
       "0  Враньё! Температуры горения хватит чтобы ее ра...  False  True   fluent  \n",
       "1                    А ты там был? Ты вообще служил?  False  True   fluent  \n",
       "2                              а сам где кормишься ?  False  True   fluent  \n",
       "3  Этому сайту давно не доверяю, пишут всякую ерунду  False  True   fluent  \n",
       "4       у меня сегодня подобный звонок был. Достали.  False  True   fluent  "
      ]
     },
     "execution_count": 46,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "df.head(5)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Получение метрик"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Оценка будет производиться на основе трех метрик:\n",
    "- style accuracy\n",
    "- meaning preservation\n",
    "- joint fluency\n",
    "\n",
    "Каждая метрика получается из предиктов отдельной языковой модели по имеющимся текстам, прошедшим фильтрацию. Поверх отдельных метрик строится финальная метрика - Joint score, которая аггрегирует три предыдущих."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "res_df = pd.read_csv('dataset.csv')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>instruction</th>\n",
       "      <th>inputs</th>\n",
       "      <th>outputs</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>Есть токсичный ответ: \"{toxic_comment}\"\\r\\nПер...</td>\n",
       "      <td>пиздеж! температуры горения хватит чтобы её ра...</td>\n",
       "      <td>Враньё! Температуры горения хватит чтобы ее ра...</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>Дано исходное высказывание: \"{toxic_comment}\"\\...</td>\n",
       "      <td>а ты чмо там был.ты вообще служил.гандон</td>\n",
       "      <td>А ты там был? Ты вообще служил?</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>Токсичное утверждение: \"{toxic_comment}\"\\r\\nПе...</td>\n",
       "      <td>пиздабол ---- а сам где кормишься ?</td>\n",
       "      <td>а сам где кормишься ?</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>Токсичное сообщение: \"{toxic_comment}\"\\r\\nПрео...</td>\n",
       "      <td>этому сайту я давно не доверяю, пишут разную х...</td>\n",
       "      <td>Этому сайту давно не доверяю, пишут всякую ерунду</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>Токсичный комментарий: \"{toxic_comment}\"\\r\\nИз...</td>\n",
       "      <td>у меня сегодня подобный звонок был. достали го...</td>\n",
       "      <td>у меня сегодня подобный звонок был. Достали.</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>...</th>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>795</th>\n",
       "      <td>Токсичное сообщение: \"{toxic_comment}\"\\r\\nПрео...</td>\n",
       "      <td>киргиз украл, она сбежала, так он ее зарезал с...</td>\n",
       "      <td>киргиз украл, она сбежала, так он ее зарезал</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>796</th>\n",
       "      <td>Токсичный комментарий: \"{toxic_comment}\"\\r\\nИз...</td>\n",
       "      <td>это не от того что желающих работать нет. а от...</td>\n",
       "      <td>Это не от того что желающих работать нет а от ...</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>797</th>\n",
       "      <td>Токсичное замечание: \"{toxic_comment}\"\\r\\nПере...</td>\n",
       "      <td>долбаеб, решил ресонуться, купил бы кольцо, цв...</td>\n",
       "      <td>Если хочешь показать себя , купи кольцо, цветы...</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>798</th>\n",
       "      <td>Токсичный ответ: \"{toxic_comment}\"\\r\\nПреобраз...</td>\n",
       "      <td>такому уроду пасти баранов страшно доверить а ...</td>\n",
       "      <td>такому плохому  человеку ничего нельзя доверит...</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>799</th>\n",
       "      <td>Токсичное мнение: \"{toxic_comment}\"\\r\\nПерефор...</td>\n",
       "      <td>самий пидараз здесь бартамео он уйдёть всё буд...</td>\n",
       "      <td>Самий нехороший, здесь бартамео он уйдет и всё...</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "<p>800 rows × 3 columns</p>\n",
       "</div>"
      ],
      "text/plain": [
       "                                           instruction  \\\n",
       "0    Есть токсичный ответ: \"{toxic_comment}\"\\r\\nПер...   \n",
       "1    Дано исходное высказывание: \"{toxic_comment}\"\\...   \n",
       "2    Токсичное утверждение: \"{toxic_comment}\"\\r\\nПе...   \n",
       "3    Токсичное сообщение: \"{toxic_comment}\"\\r\\nПрео...   \n",
       "4    Токсичный комментарий: \"{toxic_comment}\"\\r\\nИз...   \n",
       "..                                                 ...   \n",
       "795  Токсичное сообщение: \"{toxic_comment}\"\\r\\nПрео...   \n",
       "796  Токсичный комментарий: \"{toxic_comment}\"\\r\\nИз...   \n",
       "797  Токсичное замечание: \"{toxic_comment}\"\\r\\nПере...   \n",
       "798  Токсичный ответ: \"{toxic_comment}\"\\r\\nПреобраз...   \n",
       "799  Токсичное мнение: \"{toxic_comment}\"\\r\\nПерефор...   \n",
       "\n",
       "                                                inputs  \\\n",
       "0    пиздеж! температуры горения хватит чтобы её ра...   \n",
       "1             а ты чмо там был.ты вообще служил.гандон   \n",
       "2                  пиздабол ---- а сам где кормишься ?   \n",
       "3    этому сайту я давно не доверяю, пишут разную х...   \n",
       "4    у меня сегодня подобный звонок был. достали го...   \n",
       "..                                                 ...   \n",
       "795  киргиз украл, она сбежала, так он ее зарезал с...   \n",
       "796  это не от того что желающих работать нет. а от...   \n",
       "797  долбаеб, решил ресонуться, купил бы кольцо, цв...   \n",
       "798  такому уроду пасти баранов страшно доверить а ...   \n",
       "799  самий пидараз здесь бартамео он уйдёть всё буд...   \n",
       "\n",
       "                                               outputs  \n",
       "0    Враньё! Температуры горения хватит чтобы ее ра...  \n",
       "1                      А ты там был? Ты вообще служил?  \n",
       "2                                а сам где кормишься ?  \n",
       "3    Этому сайту давно не доверяю, пишут всякую ерунду  \n",
       "4         у меня сегодня подобный звонок был. Достали.  \n",
       "..                                                 ...  \n",
       "795       киргиз украл, она сбежала, так он ее зарезал  \n",
       "796  Это не от того что желающих работать нет а от ...  \n",
       "797  Если хочешь показать себя , купи кольцо, цветы...  \n",
       "798  такому плохому  человеку ничего нельзя доверит...  \n",
       "799  Самий нехороший, здесь бартамео он уйдет и всё...  \n",
       "\n",
       "[800 rows x 3 columns]"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "res_df"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "inputs = res_df['inputs'].tolist()\n",
    "refs = res_df['outputs'].tolist()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "style_model, style_tokenizer = load_model(\"IlyaGusev/rubertconv_toxic_clf\")\n",
    "meaning_model, meaning_tokenizer = load_model(\"s-nlp/rubert-base-cased-conversational-paraphrase-v1\")\n",
    "cola_model, cola_tolenizer = load_model(\"s-nlp/ruRoberta-large-RuCoLa-v1\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [],
   "source": [
    "# inputs = df['init'].tolist()\n",
    "# refs = df['rewrite'].tolist()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Чтобы приблизить оценку к человеческой были обучены специальные калибраторы, которые перевзвешивают получившиеся предикты языковых моделей для получения более честной оценки."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "C:\\Users\\artem\\AppData\\Roaming\\Python\\Python311\\site-packages\\sklearn\\base.py:347: InconsistentVersionWarning: Trying to unpickle estimator IsotonicRegression from version 1.0 when using version 1.3.0. This might lead to breaking code or invalid results. Use at your own risk. For more info please refer to:\n",
      "https://scikit-learn.org/stable/model_persistence.html#security-maintainability-limitations\n",
      "  warnings.warn(\n"
     ]
    }
   ],
   "source": [
    "with open(\"score_calibrations_ru.pkl\", \"rb\") as f:\n",
    "    style_calibrator = pickle.load(f)\n",
    "    content_calibrator = pickle.load(f)\n",
    "    fluency_calibrator = pickle.load(f)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Делаем замер метрик."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Style evaluation\n",
      "Meaning evaluation\n",
      "Fluency evaluation\n",
      "Style accuracy:       0.9322659969329834\n",
      "Meaning preservation: 0.9376146197319031\n",
      "Joint fluency:        0.7979023456573486\n",
      "Joint score:          0.6902967691421509\n",
      "Scores after calibration:\n",
      "Style accuracy:       0.7582746361404575\n",
      "Meaning preservation: 0.7277323379065783\n",
      "Joint fluency:        0.8220418469806109\n",
      "Joint score:          0.4468312980425138\n"
     ]
    }
   ],
   "source": [
    "res = evaluate_style_transfer(\n",
    "    original_texts=inputs,\n",
    "    rewritten_texts=refs,\n",
    "    style_model=style_model,\n",
    "    style_tokenizer=style_tokenizer,\n",
    "    meaning_model=meaning_model,\n",
    "    meaning_tokenizer=meaning_tokenizer,\n",
    "    cola_model=cola_model,\n",
    "    cola_tokenizer=cola_tolenizer,\n",
    "    style_target_label=0,\n",
    "    aggregate=True,\n",
    "    style_calibration=lambda x: style_calibrator.predict(x[:, np.newaxis]),\n",
    "    meaning_calibration=lambda x: content_calibrator.predict(x[:, np.newaxis]),\n",
    "    fluency_calibration=lambda x: fluency_calibrator.predict(x[:, np.newaxis]),\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 50,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Style evaluation\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Meaning evaluation\n",
      "Fluency evaluation\n",
      "Style accuracy:       0.9482819437980652\n",
      "Meaning preservation: 0.9316206574440002\n",
      "Joint fluency:        0.8761930465698242\n",
      "Joint score:          0.7697036862373352\n",
      "Scores after calibration:\n",
      "Style accuracy:       0.7789858852900526\n",
      "Meaning preservation: 0.7183928820164345\n",
      "Joint fluency:        0.8623976766936412\n",
      "Joint score:          0.47682560625592224\n"
     ]
    }
   ],
   "source": [
    "res = evaluate_style_transfer(\n",
    "    original_texts=inputs,\n",
    "    rewritten_texts=refs,\n",
    "    style_model=style_model,\n",
    "    style_tokenizer=style_tokenizer,\n",
    "    meaning_model=meaning_model,\n",
    "    meaning_tokenizer=meaning_tokenizer,\n",
    "    cola_model=cola_model,\n",
    "    cola_tokenizer=cola_tolenizer,\n",
    "    style_target_label=0,\n",
    "    aggregate=True,\n",
    "    style_calibration=lambda x: style_calibrator.predict(x[:, np.newaxis]),\n",
    "    meaning_calibration=lambda x: content_calibrator.predict(x[:, np.newaxis]),\n",
    "    fluency_calibration=lambda x: fluency_calibrator.predict(x[:, np.newaxis]),\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "`Joint score = 0.477`"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.11.4"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
